#pragma once
#include "mesh.h"
#include <vector>
#include <iostream>
#include "../math/vector.h"
#include "../math/matrix.h"

class UniformMesh : public Mesh {
public:
    UniformMesh() {}
    UniformMesh(int ex, int ey, int ez, Vector3 extents);

    Vector3 node_coord(int i) const;
    void  element_coords(int i, Matrix &XIe) const;
    const std::vector<int>& element_nodes(int i) const;

    int num_nodes()    const { return _nn; }
    int num_elements() const { return _ne; }

private:
    int    _ex, _ey, _ez, _ne;
    int    _nx, _ny, _nz, _nn;
    double _lx, _ly, _lz;
    double _dx, _dy, _dz;
};

// Computes the global node numbers of an element 
// from the element id.  Assumes x varies first.
inline const std::vector<int>& UniformMesh::element_nodes(int i) const {
    int j   = i / _ex;
    int k   = j / _ey;        
    int nxy = _nx*_ny;
    int n   = (i-j*_ex) + (j-k*_ex)*_nx + k*nxy;

	static std::vector<int> conn(8);
    conn[0] = n;			conn[4]=conn[0]+nxy;
	conn[1] = n+1;			conn[5]=conn[1]+nxy;
	conn[2] = n+1+_nx;		conn[6]=conn[2]+nxy;
	conn[3] = n+_nx;	  	conn[7]=conn[3]+nxy;				
	return conn;
}

// Returns a 8x3 matrix of the element nodal coordinates.
inline void UniformMesh::element_coords(int i, Matrix &XIe) const {    
    const auto &conn = element_nodes(i);
    for (int j=0; j<conn.size(); ++j)
        XIe.set_row(j, node_coord(conn[j]));
}

// Returns the nodal coordinates of an element.
inline Vector3 UniformMesh::node_coord(int i) const {    
    int j = i/_nx;
    int k = j/_ny;
    i -= j*_nx;  // Same as i =  i%_nx
    j -= k*_ny;  // Same as j = (i/_nx) % _ny
    
    return Vector3(_dx*double(i), _dy*double(j), _dz*double(k));
}
